package com.haojin.bean;

import com.haojin.anno.Bean;
import com.haojin.anno.Di;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author .29.
 * @create 2023-02-03 18:00
 */
public class AnnotationApplicationContext implements ApplicationContext{
    //创建一个Map集合，方式Bean的实例对象
    private Map<Class,Object> beanFactory = new HashMap<>();
    //
    private static String PartName;

    //返回创建的示例对象
    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }

    //设置包扫描的规则
    //当前包及其子包那个有@Bean注解，把这个类通过反射实例化

    public AnnotationApplicationContext(String basePackage) {

        try {
            //1. 把 . 替换成 \
            String packagePath = basePackage.replaceAll("\\.", "\\\\");

            //2. 获取包的绝对路径
            Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packagePath);
            while(urls.hasMoreElements()){//得到具体值时
                //得到绝对路径
                URL url = urls.nextElement();
                //绝对路径使用UTF-8格式
                String filePath = URLDecoder.decode(url.getFile(), "utf-8");
                //截取包名前的部分
                PartName = filePath.substring(0,filePath.length()- packagePath.length());

                //3.包的扫描
                loadBean(new File(filePath));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        //属性的注入
        loadDi();
    }



    //包扫描过程，实现bean实例化
    private void loadBean(File file) throws Exception {
        //1. 判断是否为文件夹
        if(file.isDirectory()) {
            //2. 获取文件夹内容
            File[] childrenFiles = file.listFiles();
            //3. 判断文件夹内容是否为空，空则直接返回
            if (childrenFiles == null || childrenFiles.length == 0) {
                return;
            }
            //4. 如果文件夹不为空，遍历内容
            for(File c : childrenFiles) {
                //4.1 遍历得到每个File对象，继续判断，如果还是文件夹，递归
                if(c.isDirectory()){
                    //递归
                    loadBean(c);
                }else{
                    //4.2 遍历得到File对象不是文件夹，是文件
                    //4.3 就获取 包路径 + 类名称部分 --- 字符串截取
                    String pathWithClass = c.getAbsolutePath().substring(PartName.length() - 1);//getAbsolutePath()获取路径，截取包路径+类名

                    //4.4 判断文件是否为.class文件
                    if(pathWithClass.contains(".class")){
                        //4.5 是.class文件时，将 \ 换成 .  同时把.class去除（获取全类名）
                        String allName = pathWithClass.replaceAll("\\\\", ".").replaceAll(".class", "");
                        //4.6 判断类是否存在注解@Bean,如果有则进行实例化
                        //4.6.1 获取类的Class
                        Class<?> clazz = Class.forName(allName);
                        //4.6.2 判断不是接口
                        if(!clazz.isInterface()){
                            //4.6.3判断是否有@Bean注解
                            Bean annotation = clazz.getAnnotation(Bean.class);

                            if(annotation != null) {
                                //4.6.4 有，则实例化
                                Object instance = clazz.getConstructor().newInstance();

                                //4.7 实例化后，对象存放到Map集合beanFactory中去
                                //4.7.1 判断当前类是否有接口，让接口class作为map集合的key
                                if(clazz.getInterfaces().length > 0){
                                    beanFactory.put(clazz.getInterfaces()[0],instance);
                                }else{
                                    //没有接口，当前类的class对象作为key
                                    beanFactory.put(clazz,instance);

                                }
                            }
                        }


                    }




                }
            }


        }
    }

    //属性注入
    private void loadDi() {
        //实例化对象都存放在beanFactory这个map集合中
        //1. 遍历beanFactory的map集合，
        Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();
        for(Map.Entry<Class, Object> entry : entries){
            //2. 获取是每个对象（value），以及每个对象的属性
            Object value = entry.getValue();

            //获取对象的Class
            Class clazz = value.getClass();


            Field[] fields = clazz.getDeclaredFields();
            //3. 遍历得到每个对象的属性数组，从而得到每个属性
            for(Field f : fields){
                //4. 判断属性上面是否有@Di注解
                Di annotation = f.getAnnotation(Di.class);
                if(annotation != null){
                    //如果是私有属性，需要设置-属性可修改
                    f.setAccessible(true);

                    //5. 如果有@Di注解，将对象进行设置（注入）
                    try {
                        f.set(value,beanFactory.get(f.getType()));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }

                }
            }




        }






    }

    public static void main(String[] args) {
        new AnnotationApplicationContext("com.haojin");
    }
}
